#include <iostream>
#include <sstream>
#include <fstream>
using namespace std;


void show_state(std::ios& stream){
    if (stream.good())
        cout << " good ";
    if (stream.bad())
        cout << " bad ";
    if (stream.fail())
        cout << " fail ";
    if (stream.eof())
        cout << " eof ";
    cout << endl;
}


void TEST_ISTREAM(){
    printf("=============================== %s ==================================\n", __func__);
    istringstream ss;
    string str;

    // setstate, clear 设置状态与清除状态
    ss.setstate(ios_base::badbit); show_state(ss); // eof
    ss.clear(); show_state(ss); // good

    // operator= 格式化输入
    cout << endl;
    ss = istringstream("123.3");
    float a;
    ss >> a;
    cout << a; show_state(ss); // 123.3 eof

    ss = istringstream("123.3 "); // 末尾多了一个空格
    ss >> a;
    cout << a; show_state(ss); // 123.3 good

    // get() 获取一个字符
    cout << endl;
    ss = istringstream("abc");
    cout << (char)ss.get(); show_state(ss); // a good
    cout << (char)ss.get(); show_state(ss); // b good
    cout << (char)ss.get(); show_state(ss); // c good
    cout << (char)ss.get(); show_state(ss); // fail eof

    // peek() 读出一个字符，但不释出
    cout << endl;
    ss = istringstream("abc");
    cout << (char)ss.peek(); show_state(ss); // a good
    cout << (char)ss.peek(); show_state(ss); // a good

    ss = istringstream(""); show_state(ss); // good
    ss.peek(); show_state(ss); // eof

    // getline() 读入到字符串，直到遇到分隔符
    cout << endl;
    ss = istringstream("a|ab|abc|abcd");
    getline(ss, str, '|'); cout << str; show_state(ss); // good
    getline(ss, str, '|'); cout << str; show_state(ss); // good
    getline(ss, str, '|'); cout << str; show_state(ss); // good
    getline(ss, str, '|'); cout << str; show_state(ss); // eof

    // gcount() 返回“无格式输入”所释出的字符个数
    string buffer(100, '\0');
    ss = istringstream("123456");
    ss.read(&buffer[0], buffer.size());
    cout << &buffer[0] << " " << ss.gcount() << endl; // 123456 6

    // tellg() 返回streambuf的输入位置指示器
    cout << endl;
    ss = istringstream("123"); cout << ss.tellg() << endl; // 0
    ss.get(); cout << ss.tellg() << endl; // 1
    ss.get(); cout << ss.tellg() << endl; // 2
    ss.get(); cout << ss.tellg() << endl; // 3
    ss.get(); cout << ss.tellg() << endl; // -1

    // seekg() 设置streambuf的输入位置指示器
    cout << endl;
    ss = istringstream("01234567");
    // 移动到绝对位置
    ss.seekg(4); cout << (char)ss.peek() << endl; // 4
    ss.seekg(6); cout << (char)ss.peek() << endl; // 6
    ss.seekg(8); cout << (char)ss.peek(); show_state(ss); // <ff> eof
    ss.seekg(0); cout << (char)ss.peek(); show_state(ss); // 0 good
    // 相对移动: 流的开始，流的结尾，流的当前位置
    ss.seekg(2, std::ios_base::beg); cout << (char)ss.peek() << endl; // 2
    ss.seekg(2, std::ios_base::cur); cout << (char)ss.peek() << endl; // 4
    ss.seekg(0, std::ios_base::end); cout << (char)ss.peek(); show_state(ss); // <ff> eof
    ss.seekg(-1, std::ios_base::end); cout << (char)ss.peek(); show_state(ss); // 7 good

    // swap() 交换两个流
    cout << endl;
    ss = istringstream("1234");
    istringstream ss2("7890");
    ss.swap(ss2);
    ss >> str; cout << str << endl; // 7890
    ss2 >> str; cout << str << endl; // 1234
}

void TEST_SSTREAM(){
    printf("=============================== %s ==================================\n", __func__);
    // stringstream有两个指针，左指针负责读取，右指针负责写入
    stringstream ss;
    int a;
    ss << "90 "; // 注意最后有一个空格
    cout << ss.tellg() << ", " << ss.tellp() << endl; // 0, 3
    ss >> a;
    cout << ss.tellg() << ", " << ss.tellp() << endl; // 2, 3
    cout << a << endl;

    ss << "80";
    cout << ss.tellg() << ", " << ss.tellp() << endl; // 2, 5
    ss >> a;
    cout << ss.tellg() << ", " << ss.tellp() << endl; // -1, -1, 当两个指针重合时，状态不再是good，除非clear
    cout << a << endl;

    ss.clear();
    cout << ss.tellg() << ", " << ss.tellp() << endl; // 5, 5

    cout << ss.str() << endl;
}


void TEST_STRING(){
    printf("=============================== %s ==================================\n", __func__);
    string str(100, '\0');
    char* pstr = &str[0]; // 可以直接获取string内部的指针
    cout << pstr << endl;

    for (int i=0; i<str.size(); ++i)
        pstr[i] = '0';
    cout << str << endl;
}


void TEST_BOOL(){
    printf("=============================== %s ==================================\n", __func__);
    // IO控制的类可以直接
    istringstream ss("123 456");
    for (int a; ss >> a;){
        cout << a; show_state(ss);
    }
    show_state(ss);
}


void TEST_STATE(){
    printf("=============================== %s ==================================\n", __func__);
    // IO流的状态由3个bit构成
    cout << ios_base::goodbit << endl; // 0
    cout << ios_base::badbit << endl; // 1
    cout << ios_base::eofbit << endl; // 2
    cout << ios_base::failbit << endl; // 4
}

void system_call(const char * cmd){
    cout << cmd << endl;
    system(cmd);
    cout << endl;
}

void TEST_FSTREAM(){
    printf("=============================== %s ==================================\n", __func__); cout << std::flush;
    string filename = "matrix.csv";

    // out
    ofstream ofs(filename, ios_base::out);
    ofs << "1, 2, 3, 4, 5" << endl;
    ofs.close();
    system_call("cat matrix.csv");

    // out | app
    ofs.open("matrix.csv", ios_base::out | ios_base::app);
    ofs << "1, 2, 3, 4, 5" << endl;
    ofs.close();
    system_call("cat matrix.csv");

    // out
    ofs.open(filename, ios_base::out); // 不使用app模式打开文件会删除原来的文件
    ofs << "1, 2, 3, 4, 5" << endl;
    ofs.close();
    system_call("cat matrix.csv");

    // out | ate
    ofs.open(filename, ios_base::out | ios_base::ate); // 不使用app模式打开文件会删除原来的文件
    ofs << "1, 2, 3, 4, 5" << endl;
    ofs.close();
    system_call("cat matrix.csv");

    // in
    ifstream ifs(filename, ios_base::in);
    for (int x; ifs >> x;)
    {
        cout << x << endl;
        ifs.seekg(1, ios_base::cur); // "向右移动一位，跳过逗号"
    }
    ifs.close();

    // in | out
    cout << endl;
    fstream fs(filename, ios_base::out | ios_base::in); // 同时打开in和out，原文件不会删除
    for (int x; fs >> x;)
    {
        cout << x << endl;
        fs.seekg(1, ios_base::cur); // "向右移动一位，跳过逗号"
    }
    fs.close();
    system_call("cat matrix.csv");

    // in | out
    fs.open(filename, ios_base::out | ios_base::in);
    cout << fs.tellg() << ", " << fs.tellp() << endl; // 0, 0  读取和写入的初始位置都为文件开头
    fs.seekp(3); fs.put('4'); // 将文件的第3个字符'2'修改为'4'
    cout << fs.tellg() << ", " << fs.tellp() << endl; // 4, 4  文件的读取和写入指针保持指向同一个位置
    fs.close();
    system_call("cat matrix.csv");

    // 删除文件
    std::remove(filename.c_str());
}


int main(){
    TEST_STATE();
    TEST_STRING();
    TEST_ISTREAM();
    TEST_SSTREAM();
    TEST_BOOL();
    TEST_FSTREAM();
    return 0;
}
